<!DOCTYPE html>
<html lang="en">

<!-- Head tag -->
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="google-site-verification" content="xBT4GhYoi5qRD5tr338pgPM5OWHHIDR6mNg1a3euekI" />
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="">
    <meta name="keyword"  content="">
    <link rel="shortcut icon" href="/img/favicon.ico">

    <title>
        
          webpack2 终极优化 - 浩麟的博客
        
    </title>

    <link rel="canonical" href="https://wuhaolin.cn/2017/04/30/webpack2 终极优化/">

    <!-- Bootstrap Core CSS -->
    
<link rel="stylesheet" href="/css/bootstrap.min.css">


    <!-- Custom CSS -->
    
<link rel="stylesheet" href="/css/hux-blog.min.css">


    <!-- Pygments Highlight CSS -->
    
<link rel="stylesheet" href="/css/highlight.css">


    <!-- Custom Fonts -->
    <!-- <link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css" rel="stylesheet" type="text/css"> -->
    <!-- Hux change font-awesome CDN to qiniu -->
    <link href="https://cdn.staticfile.org/font-awesome/4.5.0/css/font-awesome.min.css" rel="stylesheet" type="text/css">


    <!-- Hux Delete, sad but pending in China
    <link href='http://fonts.googleapis.com/css?family=Lora:400,700,400italic,700italic' rel='stylesheet' type='text/css'>
    <link href='http://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800' rel='stylesheet' type='text/
    css'>
    -->


    <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
        <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
        <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
    <![endif]-->

    <!-- ga & ba script hoook -->
    <script></script>
<meta name="generator" content="Hexo 4.1.1"></head>


<!-- hack iOS CSS :active style -->
<body ontouchstart="">

<!-- Navigation -->
<nav class="navbar navbar-default navbar-custom navbar-fixed-top">
    <div class="container-fluid">
        <!-- Brand and toggle get grouped for better mobile display -->
        <div class="navbar-header page-scroll">
            <button type="button" class="navbar-toggle">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="/">浩麟的博客</a>
        </div>

        <!-- Collect the nav links, forms, and other content for toggling -->
        <!-- Known Issue, found by Hux:
            <nav>'s height woule be hold on by its content.
            so, when navbar scale out, the <nav> will cover tags.
            also mask any touch event of tags, unfortunately.
        -->
        <div id="huxblog_navbar">
            <div class="navbar-collapse">
                <ul class="nav navbar-nav navbar-right">
                    <li>
                        <a href="/">Home</a>
                    </li>
                    <li>
                        <a href="https://github.com/gwuhaolin/blog/" target="_blank">订阅</a>
                    </li>

                    

                        
                    

                        
                        <li>
                            <a href="/about/">关于</a>
                        </li>
                        
                    

                        
                        <li>
                            <a href="/archives/">归档</a>
                        </li>
                        
                    

                        
                        <li>
                            <a href="/tags/">标签</a>
                        </li>
                        
                    

                </ul>
            </div>
        </div>
        <!-- /.navbar-collapse -->
    </div>
    <!-- /.container -->
</nav>
<script>
    // Drop Bootstarp low-performance Navbar
    // Use customize navbar with high-quality material design animation
    // in high-perf jank-free CSS3 implementation
    var $body   = document.body;
    var $toggle = document.querySelector('.navbar-toggle');
    var $navbar = document.querySelector('#huxblog_navbar');
    var $collapse = document.querySelector('.navbar-collapse');

    $toggle.addEventListener('click', handleMagic)
    function handleMagic(e){
        if ($navbar.className.indexOf('in') > 0) {
        // CLOSE
            $navbar.className = " ";
            // wait until animation end.
            setTimeout(function(){
                // prevent frequently toggle
                if($navbar.className.indexOf('in') < 0) {
                    $collapse.style.height = "0px"
                }
            },400)
        }else{
        // OPEN
            $collapse.style.height = "auto"
            $navbar.className += " in";
        }
    }
</script>


<!-- Main Content -->

<!-- Image to hack wechat -->
<!-- <img src="https://wuhaolin.cn/img/icon_wechat.png" width="0" height="0"> -->
<!-- <img src="{{ site.baseurl }}/{% if page.header-img %}{{ page.header-img }}{% else %}{{ site.header-img }}{% endif %}" width="0" height="0"> -->

<!-- Post Header -->
<style type="text/css">
    header.intro-header{
        background-image: url('/img/bg.jpg')
    }
</style>
<header class="intro-header" >
    <div class="container">
        <div class="row">
            <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
                <div class="post-heading">
                    <div class="tags">
                        
                          <a class="tag" href="/tags/#webpack" title="webpack">webpack</a>
                        
                    </div>
                    <h1>webpack2 终极优化</h1>
                    <h2 class="subheading"></h2>
                    <span class="meta">
                        Posted by 吴浩麟 on
                        2017-04-30
                    </span>
                </div>
            </div>
        </div>
    </div>
</header>

<!-- Post Content -->
<article>
    <div class="container">
        <div class="row">
    <!-- Post Container -->
            <div class="
                col-lg-8 col-lg-offset-2
                col-md-10 col-md-offset-1
                post-container">

                <p><a href="http://webpack.wuhaolin.cn/" target="_blank" rel="noopener"><img src="http://ou8vcvyuy.bkt.clouddn.com/dive-into-webpack-for-blog.jpg" alt=""></a></p>
<p>webpack是当下最流行的js打包工具，这得益于网页应用日益复杂和js模块化的流行。<a href="https://webpack.js.org" target="_blank" rel="noopener">webpack2</a>增加了一些新特性也正式发布了一段时间，是时候告诉大家如何用webpack2优化你的构建让它构建出更小的文件尺寸和更好的开发体验。</p>
<h1 id="优化输出"><a href="#优化输出" class="headerlink" title="优化输出"></a>优化输出</h1><p>打包结果更小可以让网页打开速度更快以及简约宽带。可以通过这以下几点做到</p>
<h4 id="压缩css"><a href="#压缩css" class="headerlink" title="压缩css"></a>压缩css</h4><p><code>css-loader</code> 在webpack2里默认是没有开启压缩的，最后生成的css文件里有很多空格和tab，通过配置<br><code>css-loader?minimize</code>参数可以开启压缩输出最小的css。css的压缩实际是是通过<a href="http://cssnano.co" target="_blank" rel="noopener">cssnano</a>实现的。</p>
<h4 id="tree-shaking"><a href="#tree-shaking" class="headerlink" title="tree-shaking"></a>tree-shaking</h4><p>tree-shaking 是指借助es6 <code>import export</code> 语法静态性的特点来删掉export但是没有import过的东西。要让tree-shaking工作需要注意以下几点：</p>
<ul>
<li><p>配置babel让它在编译转化es6代码时不把<code>import export</code>转换为cmd的<code>module.export</code>，配置如下：</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">"presets": [</span><br><span class="line">    [</span><br><span class="line">      <span class="string">"es2015"</span>,</span><br><span class="line">      &#123;</span><br><span class="line">        <span class="attr">"modules"</span>: <span class="literal">false</span></span><br><span class="line">      &#125;</span><br><span class="line">    ]</span><br><span class="line">]</span><br></pre></td></tr></table></figure>
</li>
<li><p>大多数分布到npm的库里的代码都是es5的，但是也有部分库（redux,react-router等等）开始支持tree-shaking。这些库发布到npm里的代码即包含es5的又包含全采用了es6 <code>import export</code> 语法的代码。<br>拿redux库来说，npm下载到的目录结构如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">├── es</span><br><span class="line">│   └── utils</span><br><span class="line">├── lib</span><br><span class="line">│   └── utils</span><br></pre></td></tr></table></figure>
</li>
</ul>
<p>其中lib目录里是编译出的es5代码，es目录里是编译出的采用<code>import export</code> 语法的es5代码，在redux的<code>package.json</code>文件里有这两个配置：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">&quot;main&quot;: &quot;lib/index.js&quot;,</span><br><span class="line">&quot;jsnext:main&quot;: &quot;es/index.js&quot;,</span><br></pre></td></tr></table></figure></p>
<p>这是指这个库的入口文件的位置，所以要让webpack去读取es目录下的代码需要使用jsnext:main字段配置的入口，要做到这点webpack需要这样配置：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">	resolve: &#123;</span><br><span class="line">            mainFields: [<span class="string">'jsnext:main'</span>,<span class="string">'main'</span>],</span><br><span class="line">        &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></p>
<p>这会让webpack先使用jsnext:main字段，在没有时使用main字段。这样就可以优化支持tree-shaking的库。</p>
<h4 id="优化-UglifyJsPlugin"><a href="#优化-UglifyJsPlugin" class="headerlink" title="优化 UglifyJsPlugin"></a>优化 UglifyJsPlugin</h4><p>webpack <code>--optimize-minimize</code> 选项会开启 UglifyJsPlugin来压缩输出的js，但是默认的UglifyJsPlugin配置并没有把代码压缩到最小输出的js里还是有注释和空格，需要覆盖默认的配置：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">new</span> UglifyJsPlugin(&#123;</span><br><span class="line">    <span class="comment">// 最紧凑的输出</span></span><br><span class="line">    beautify: <span class="literal">false</span>,</span><br><span class="line">    <span class="comment">// 删除所有的注释</span></span><br><span class="line">    comments: <span class="literal">false</span>,</span><br><span class="line">    compress: &#123;</span><br><span class="line">      <span class="comment">// 在UglifyJs删除没有用到的代码时不输出警告  </span></span><br><span class="line">      warnings: <span class="literal">false</span>,</span><br><span class="line">      <span class="comment">// 删除所有的 `console` 语句</span></span><br><span class="line">      <span class="comment">// 还可以兼容ie浏览器</span></span><br><span class="line">      drop_console: <span class="literal">true</span>,</span><br><span class="line">      <span class="comment">// 内嵌定义了但是只用到一次的变量</span></span><br><span class="line">      collapse_vars: <span class="literal">true</span>,</span><br><span class="line">      <span class="comment">// 提取出出现多次但是没有定义成变量去引用的静态值</span></span><br><span class="line">      reduce_vars: <span class="literal">true</span>,</span><br><span class="line">    &#125;</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure></p>
<h4 id="定义环境变量-NODE-ENV-production"><a href="#定义环境变量-NODE-ENV-production" class="headerlink" title="定义环境变量 NODE_ENV=production"></a>定义环境变量 NODE_ENV=production</h4><p>很多库里（比如react）有部分代码是这样的：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span>(process.env.NODE_ENV !== <span class="string">'production'</span>)&#123;</span><br><span class="line"><span class="comment">// 不是生产环境才需要用到的代码，比如控制台里看到的警告    </span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>在环境变量 <code>NODE_ENV</code> 等于 <code>production</code> 的时候UglifyJs会认为if语句里的是死代码在压缩代码时删掉。</p>
<h4 id="使用-CommonsChunkPlugin-抽取公共代码"><a href="#使用-CommonsChunkPlugin-抽取公共代码" class="headerlink" title="使用 CommonsChunkPlugin 抽取公共代码"></a>使用 CommonsChunkPlugin 抽取公共代码</h4><p><a href="https://webpack.github.io/docs/list-of-plugins.html#commonschunkplugin" target="_blank" rel="noopener">CommonsChunkPlugin</a>可以提取出多个代码块都依赖的模块形成一个单独的模块。要发挥CommonsChunkPlugin的作用还需要浏览器缓存机制的配合。在应用有多个页面的场景下提取出所有页面公共的代码减少单个页面的代码，在不同页面之间切换时所有页面公共的代码之前被加载过而不必重新加载。这个方法可以非常有效的提升应用性能。</p>
<h4 id="在生产环境按照文件内容md5打hash"><a href="#在生产环境按照文件内容md5打hash" class="headerlink" title="在生产环境按照文件内容md5打hash"></a>在生产环境按照文件内容md5打hash</h4><p>webpack编译在生产环境出来的js、css、图片、字体这些文件应该放到CDN上，再根据文件内容的md5命名文件，利用缓存机制用户只需要加载一次，第二次加载时就直接访问缓存。如果你之后有修改就会为对应的文件生产新的md5值。做到以上你需要这样配置：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  output: &#123;</span><br><span class="line">    publicPath: CND_URL,</span><br><span class="line">    filename: <span class="string">'[name]_[chunkhash].js'</span>,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>知道以上原理后我们还可以进一步优化：利用CommonsChunkPlugin提取出使用页面都依赖的基础运行环境。比如对于最常见的react体系你可以抽出基础库<code>react</code> <code>react-dom</code> <code>redux</code> <code>react-redux</code>到一个单独的文件而不是和其它文件放在一起打包为一个文件，这样做的好处是只要你不升级他们的版本这个文件永远不会被刷新。如果你把这些基础库和业务代码打包在一个文件里每次改动业务代码都会导致浏览器重复下载这些包含基础库的代码。以上的配置为：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// vender.js 文件抽离基础库到单独的一个文件里防止跟随业务代码被刷新</span></span><br><span class="line"><span class="comment">// 所有页面都依赖的第三方库</span></span><br><span class="line"><span class="comment">// react基础</span></span><br><span class="line"><span class="keyword">import</span> <span class="string">'react'</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="string">'react-dom'</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="string">'react-redux'</span>;</span><br><span class="line"><span class="comment">// redux基础</span></span><br><span class="line"><span class="keyword">import</span> <span class="string">'redux'</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="string">'redux-thunk'</span>;</span><br></pre></td></tr></table></figure></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// webpack配置</span></span><br><span class="line">&#123;</span><br><span class="line">  entry: &#123;</span><br><span class="line">    vendor: <span class="string">'./path/to/vendor.js'</span>,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h4 id="DedupePlugin-和-OccurrenceOrderPlugin"><a href="#DedupePlugin-和-OccurrenceOrderPlugin" class="headerlink" title="DedupePlugin 和 OccurrenceOrderPlugin"></a>DedupePlugin 和 OccurrenceOrderPlugin</h4><p>在webpack1里经常会使用 <code>DedupePlugin</code> 插件来消除重复的模块以及使用 <code>OccurrenceOrderPlugin</code> 插件让被依赖次数更高的模块靠前分到更小的id 来达到输出更少的代码，在webpack2里这些已经这两个插件已经被移除了因为这些功能已经被内置了。</p>
<p>除了压缩文本代码外还可以：</p>
<ul>
<li><strong>用<a href="https://github.com/Klathmon/imagemin-webpack-plugin" target="_blank" rel="noopener">imagemin-webpack-plugin</a> 压缩图片</strong></li>
<li><strong>用<a href="https://github.com/mixtur/webpack-spritesmith" target="_blank" rel="noopener">webpack-spritesmith</a> 合并雪碧图</strong></li>
<li><strong>对于支持es6的js运行环境使用<a href="https://github.com/babel/babili" target="_blank" rel="noopener">babili</a></strong></li>
</ul>
<p>以上优化点只需要在构建用于生产环境代码的时候才使用，在开发环境时最好关闭因为它们很耗时。</p>
<h1 id="优化开发体验"><a href="#优化开发体验" class="headerlink" title="优化开发体验"></a>优化开发体验</h1><p>优化开发体验主要从更快的构建和更方便的功能入手。</p>
<h2 id="更快的构建"><a href="#更快的构建" class="headerlink" title="更快的构建"></a>更快的构建</h2><h4 id="缩小文件搜索范围"><a href="#缩小文件搜索范围" class="headerlink" title="缩小文件搜索范围"></a>缩小文件搜索范围</h4><p>webpack的<code>resolve.modules</code>配置模块库（通常是指node_modules）所在的位置，在js里出现<code>import &#39;redux&#39;</code>这样不是相对也不是绝对路径的写法时会去node_modules目录下找。但是默认的配置会采用向上递归搜索的方式去寻找node_modules，但通常项目目录里只有一个node_modules在项目根目录，为了减少搜索我们直接写明node_modules的全路径：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">    resolve: &#123;</span><br><span class="line">        modules: [path.resolve(__dirname, <span class="string">'node_modules'</span>)]</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></p>
<p>除此之外webpack配置loader时也可以缩小文件搜索范围。</p>
<ul>
<li>loader的test正则表达式也应该尽可能的简单，比如在你的项目里只有<code>.js</code>文件时就不要把test写成<code>/\.jsx?$/</code></li>
<li>loader使用include命中只需要处理的文件，比如babel-loader的这两个配置:</li>
</ul>
<p>只对项目目录下src目录里的代码进行babel编译<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    test: <span class="regexp">/\.js$/</span>,</span><br><span class="line">    loader: <span class="string">'babel-loader'</span>,</span><br><span class="line">    include: path.resolve(__dirname, <span class="string">'src'</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>项目目录下的所有js都会进行babel编译，包括庞大的node_modules下的js<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    test: <span class="regexp">/\.js$/</span>,</span><br><span class="line">    loader: <span class="string">'babel-loader'</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h4 id="开启-babel-loader-缓存"><a href="#开启-babel-loader-缓存" class="headerlink" title="开启 babel-loader 缓存"></a>开启 babel-loader 缓存</h4><p>babel编译过程很耗时，好在babel-loader提供缓存编译结果选项，在重启webpack时不需要创新编译而是复用缓存结果减少编译流程。babel-loader缓存机制默认是关闭的，打开的配置如下：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">    <span class="built_in">module</span>: &#123;</span><br><span class="line">         loaders: [&#123;</span><br><span class="line">                test: <span class="regexp">/\.js$/</span>,</span><br><span class="line">                loader: <span class="string">'babel-loader?cacheDirectory'</span>,</span><br><span class="line">         &#125;]</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></p>
<h4 id="使用-alias"><a href="#使用-alias" class="headerlink" title="使用 alias"></a>使用 alias</h4><p><code>resolve.alias</code> 配置路径映射。<br>发布到npm的库大多数都包含两个目录，一个是放着cmd模块化的lib目录，一个是把所有文件合成一个文件的dist目录，多数的入口文件是指向lib里面下的。<br>默认情况下webpack会去读lib目录下的入口文件再去递归加载其它依赖的文件这个过程很耗时，alias配置可以让webpack直接使用dist目录的整体文件减少文件递归解析。配置如下：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  resolve: &#123;</span><br><span class="line">    alias: &#123;</span><br><span class="line">      <span class="string">'moment'</span>: <span class="string">'moment/min/moment.min.js'</span>,</span><br><span class="line">      <span class="string">'react'</span>: <span class="string">'react/dist/react.js'</span>,</span><br><span class="line">      <span class="string">'react-dom'</span>: <span class="string">'react-dom/dist/react-dom.js'</span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></p>
<h4 id="使用-noParse"><a href="#使用-noParse" class="headerlink" title="使用 noParse"></a>使用 noParse</h4><p><code>module.noParse</code> 配置哪些文件可以脱离webpack的解析。<br>有些库是自成一体不依赖其他库的没有使用模块化的，比如jquey、momentjs、chart.js，要使用它们必须整体全部引入。<br>webpack是模块化打包工具完全没有必要去解析这些文件的依赖，因为它们都不依赖其它文件体积也很庞大，要忽略它们配置如下：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  <span class="built_in">module</span>: &#123;</span><br><span class="line">    noParse: <span class="regexp">/node_modules\/(jquey|moment|chart\.js)/</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></p>
<p>除此以外还有很多可以加速的方法：</p>
<ul>
<li><strong>使用<a href="https://github.com/amireh/happypack" target="_blank" rel="noopener">happypack</a>多进程并行构建</strong></li>
<li><strong>使用<a href="https://github.com/webpack/docs/wiki/list-of-plugins#dllplugin" target="_blank" rel="noopener">DllPlugin</a>复用模块</strong></li>
</ul>
<h2 id="更方便的功能"><a href="#更方便的功能" class="headerlink" title="更方便的功能"></a>更方便的功能</h2><h4 id="模块热替换"><a href="#模块热替换" class="headerlink" title="模块热替换"></a>模块热替换</h4><p>模块热替换是指在开发的过程中修改代码后不用刷新页面直接把变化的模块替换到老模块让页面呈现出最新的效果。<br>webpack-dev-server内置模块热替换，配置起来也很方便，下面以react应用为例，步骤如下：</p>
<ul>
<li>在启动webpack-dev-server的时候带上<code>--hot</code>参数开启模块热替换，在开启<code>--hot</code>后针对css的变化是会自动热替换的，但是js涉及到复杂的逻辑还需要进一步配置。</li>
<li>配置页面入口文件</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> App <span class="keyword">from</span> <span class="string">'./app'</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">run</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">	render(<span class="xml"><span class="tag">&lt;<span class="name">App</span>/&gt;</span></span>,<span class="built_in">document</span>.getElementById(<span class="string">'app'</span>));</span><br><span class="line">&#125;</span><br><span class="line">run();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 只在开发模式下配置模块热替换</span></span><br><span class="line"><span class="keyword">if</span> (process.env.NODE_ENV !== <span class="string">'production'</span>) &#123;</span><br><span class="line">  <span class="built_in">module</span>.hot.accept(<span class="string">'./app'</span>, run);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>当./app发生变化或者当./app依赖的文件发生变化时会把./app编译成一个模块去替换老的，替换完毕后重新执行run函数渲染出最新的效果。</p>
<h4 id="自动生成html"><a href="#自动生成html" class="headerlink" title="自动生成html"></a>自动生成html</h4><p>webpack只做了资源打包的工作还缺少把这些加载到html里运行的功能，在庞大的app里手写html去加载这些资源是很繁琐易错的，我们需要自动正确的加载打包出的资源。<br>webpack原生不支持这个功能于是我做了一个插件 <a href="https://github.com/gwuhaolin/web-webpack-plugin" target="_blank" rel="noopener">web-webpack-plugin</a><br>具体使用点开链接看<a href="https://github.com/gwuhaolin/web-webpack-plugin/blob/master/readme_zh.md" target="_blank" rel="noopener">详细文档</a>，使用大概如下：</p>
<p><a href="https://github.com/gwuhaolin/web-webpack-plugin/tree/master/demo/out-html" target="_blank" rel="noopener">demo</a></p>
<p>webpack配置<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">    entry: &#123;</span><br><span class="line">        A: <span class="string">'./a'</span>,</span><br><span class="line">        B: <span class="string">'./b'</span>,</span><br><span class="line">    &#125;,</span><br><span class="line">    plugins: [</span><br><span class="line">        <span class="keyword">new</span> WebPlugin(&#123;</span><br><span class="line">            <span class="comment">// 输出的html文件名称，必填，注意不要重名，重名会覆盖相互文件。</span></span><br><span class="line">            filename: <span class="string">'index.html'</span>,</span><br><span class="line">            <span class="comment">// 该html文件依赖的entry，必须是一个数组。依赖的资源的注入顺序按照数组的顺序。</span></span><br><span class="line">            requires: [<span class="string">'A'</span>, <span class="string">'B'</span>],</span><br><span class="line">        &#125;),</span><br><span class="line">    ]</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></p>
<p>将会输出一个<code>index.html</code>文件，这个文件将会自动引入 entry <code>A</code> 和 <code>B</code> 生成的js文件，</p>
<p>输出的html:<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;!DOCTYPE html&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">"A.js"</span>&gt;</span><span class="undefined"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">"B.js"</span>&gt;</span><span class="undefined"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure></p>
<p>输出的目录结构<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">├── A.js</span><br><span class="line">├── B.js</span><br><span class="line">└── index.html</span><br></pre></td></tr></table></figure></p>
<h4 id="管理多页面"><a href="#管理多页面" class="headerlink" title="管理多页面"></a>管理多页面</h4><p>虽然webpack适用于单页应用，但复杂的系统经常是由多个单页应用组成，每个页面一个功能模块。webpack给出了js打包方案但缺少管理多个页面的功能。 <a href="https://github.com/gwuhaolin/web-webpack-plugin" target="_blank" rel="noopener">web-webpack-plugin</a>的<code>AutoWebPlugin</code>会自动的为你的系统里每个单页应用生成一个html入口页，这个入口会自动的注入当前单页应用依赖的资源，使用它你只需如下几行代码：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">plugins: [</span><br><span class="line">    <span class="comment">// ./src/pages/ 代表存放所有页面的根目录，这个目录下的每一个目录被看着是一个单页应用</span></span><br><span class="line">    <span class="comment">// 会为里面的每一个目录生成一个html入口</span></span><br><span class="line">    <span class="keyword">new</span> AutoWebPlugin(<span class="string">'./src/pages/'</span>, &#123;</span><br><span class="line">      <span class="comment">//使用单页应用的html模版文件，这里你可以自定义配置</span></span><br><span class="line">      template: <span class="string">'./src/assets/template.html'</span>,</span><br><span class="line">    &#125;),</span><br><span class="line">],</span><br></pre></td></tr></table></figure></p>
<p>查看web-webpack-plugin的<a href="https://github.com/gwuhaolin/web-webpack-plugin/blob/master/readme_zh.md#自动探测html入口-demo" target="_blank" rel="noopener">文档了解更多</a></p>
<h3 id="分析输出结果"><a href="#分析输出结果" class="headerlink" title="分析输出结果"></a>分析输出结果</h3><p>如果你对当前的配置输出或者构建速度不满意，webpack有一个工具叫做<a href="https://webpack.github.io/analyse/" target="_blank" rel="noopener">webpack analyze</a> 以可视化的方式直观的分析构建，来进一步优化构建结果和速度。要使用它你需要在执行webpack的时候带上<code>--json --profile</code>2个参数，这代表让webpack把构建结果以json输出并带上构建性能信息，使用如下：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">webpack --json --profile &gt; stats.json</span><br></pre></td></tr></table></figure></p>
<p>会生产一个<code>stats.json</code>文件，再打开<a href="https://webpack.github.io/analyse/" target="_blank" rel="noopener">webpack analyze</a> 上传这个文件开始分析。</p>
<p><strong>最后附上这篇文章所讲到的<a href="https://gist.github.com/gwuhaolin/cebd252a23793e742e6acae90ab63e83" target="_blank" rel="noopener">webpack整体的配置</a>，分为开发环境的<code>webpack.config.js</code>和生产环境的<code>webpack-dist.config.js</code></strong></p>
<p><a href="http://wuhaolin.cn/2017/04/30/webpack2%20%E7%BB%88%E6%9E%81%E4%BC%98%E5%8C%96/">阅读原文</a></p>


                <hr>

                 <strong><a href="https://github.com/gwuhaolin/blog/issues/2" target="_blank" rel="noopener">在Github上参与本文讨论</a></strong>

                <ul class="pager">
                    
                        <li class="previous">
                            <a href="/2017/05/17/使用flv.js做直播/" data-toggle="tooltip" data-placement="top" title="使用flv.js做直播">&larr; Previous Post</a>
                        </li>
                    
                    
                        <li class="next">
                            <a href="/2017/04/30/快速优雅的为React组件生成文档/" data-toggle="tooltip" data-placement="top" title="快速优雅的为React组件生成文档">Next Post &rarr;</a>
                        </li>
                    
                </ul>
            </div>
    <!-- Side Catalog Container -->
        

    <!-- Sidebar Container -->

            <div class="
                col-lg-8 col-lg-offset-2
                col-md-10 col-md-offset-1
                sidebar-container">

                <!-- Featured Tags -->
                
                <section>
                    <!-- no hr -->
                    <h5><a href="/tags/">FEATURED TAGS</a></h5>
                    <div class="tags">
                       
                          <a class="tag" href="/tags/#webpack" title="webpack">webpack</a>
                        
                    </div>
                </section>
                

                <!-- Friends Blog -->
                
            </div>

        </div>
    </div>
</article>


<!-- async load function -->
<script>
    function async(u, c) {
      var d = document, t = 'script',
          o = d.createElement(t),
          s = d.getElementsByTagName(t)[0];
      o.src = u;
      if (c) { o.addEventListener('load', function (e) { c(null, e); }, false); }
      s.parentNode.insertBefore(o, s);
    }
</script>
<!-- anchor-js, Doc:http://bryanbraun.github.io/anchorjs/ -->
<script>
    async("https://cdn.bootcss.com/anchor-js/1.1.1/anchor.min.js",function(){
        anchors.options = {
          visible: 'always',
          placement: 'right',
          icon: '#'
        };
        anchors.add().remove('.intro-header h1').remove('.subheading').remove('.sidebar-container h5');
    })
</script>
<style>
    /* place left on bigger screen */
    @media all and (min-width: 800px) {
        .anchorjs-link{
            position: absolute;
            left: -0.75em;
            font-size: 1.1em;
            margin-top : -0.1em;
        }
    }
</style>



<!-- Footer -->
<!-- Footer -->
<footer>
    <div class="container">
        <div class="row">
            <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
                <ul class="list-inline text-center">
                    
                    
                    
                    <li>
                        <a target="_blank" href="https://www.zhihu.com/people/wu-hao-lin-67-15">
                            <span class="fa-stack fa-lg">
                                <i class="fa fa-circle fa-stack-2x"></i>
                                <i class="fa  fa-stack-1x fa-inverse">知</i>
                            </span>
                        </a>
                    </li>
                    

                    

                    

                    
                    <li>
                        <a target="_blank" href="https://github.com/gwuhaolin">
                            <span class="fa-stack fa-lg">
                                <i class="fa fa-circle fa-stack-2x"></i>
                                <i class="fa fa-github fa-stack-1x fa-inverse"></i>
                            </span>
                        </a>
                    </li>
                    

                    

                </ul>
                <p class="copyright text-muted">
                    Copyright &copy; 浩麟的博客 2019
                    <br>
                    Ported by <a href="https://github.com/gwuhaolin" target="_blank" rel="noopener">gwuhaolin</a> |
                    订阅本站<iframe
                            style="margin-left: 2px; margin-bottom:-5px;"
                            frameborder="0" scrolling="0" width="91px" height="20px"
                            src="https://ghbtns.com/github-btn.html?user=gwuhaolin&repo=blog&type=star&count=true">
                    </iframe>
                </p>
            </div>
        </div>
    </div>
</footer>

<!-- jQuery -->

<script src="/js/jquery.min.js"></script>


<!-- Bootstrap Core JavaScript -->

<script src="/js/bootstrap.min.js"></script>


<!-- Custom Theme JavaScript -->

<script src="/js/hux-blog.min.js"></script>



<!-- async load function -->
<script>
    function async(u, c) {
        var d = document, t = 'script',
            o = d.createElement(t),
            s = d.getElementsByTagName(t)[0];
        o.src = u;
        if (c) {
            o.addEventListener('load', function (e) {
                c(null, e);
            }, false);
        }
        s.parentNode.insertBefore(o, s);
    }
</script>


<!-- jquery.tagcloud.js -->
<script>
    // only load tagcloud.js in tag.html
    if ($('#tag_cloud').length !== 0) {
        async("https://wuhaolin.cn/js/jquery.tagcloud.js", function () {
            $.fn.tagcloud.defaults = {
                //size: {start: 1, end: 1, unit: 'em'},
                color: {start: '#bbbbee', end: '#0085a1'},
            };
            $('#tag_cloud a').tagcloud();
        })
    }
</script>

<!--fastClick.js -->
<script>
    async("https://cdn.bootcss.com/fastclick/1.0.6/fastclick.min.js", function () {
        var $nav = document.querySelector("nav");
        if ($nav) FastClick.attach($nav);
    })
</script>


<!-- Google Analytics -->


<script>
    // dynamic User by Hux
    var _gaId = 'UA-54964850-1';
    var _gaDomain = 'wuhaolin.cn';

    // Originial
    (function (i, s, o, g, r, a, m) {
        i['GoogleAnalyticsObject'] = r;
        i[r] = i[r] || function () {
            (i[r].q = i[r].q || []).push(arguments)
        }, i[r].l = 1 * new Date();
        a = s.createElement(o),
            m = s.getElementsByTagName(o)[0];
        a.async = 1;
        a.src = g;
        m.parentNode.insertBefore(a, m)
    })(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga');

    ga('create', _gaId, _gaDomain);
    ga('send', 'pageview');
</script>




<!-- Side Catalog -->


<!--Google Auto AD-->
<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<script>
    (adsbygoogle = window.adsbygoogle || []).push({
        google_ad_client: "ca-pub-9697944574373240",
        enable_page_level_ads: true
    });
</script>



<!-- Image to hack wechat -->
<img src="https://wuhaolin.cn /img/icon_wechat.png" width="0" height="0"/>
<!-- Migrate from head to bottom, no longer block render and still work -->
</body>

</html>
